home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 201 / 201.xpi / modules / cothread.jsm next >
Text File  |  2010-01-11  |  6KB  |  165 lines

  1. /* You may find the license in the LICENSE file */
  2.  
  3. const EXPORTED_SYMBOLS = ['CoThread', 'CoThreadListWalker'];
  4.  
  5. const Cc = Components.classes;
  6. const Ci = Components.interfaces;
  7. const Cr = Components.results;
  8.  
  9. const TYPE_REPEATING_SLACK = Ci.nsITimer.TYPE_REPEATING_SLACK;
  10. const Timer = Components.Constructor('@mozilla.org/timer;1', 'nsITimer', 'initWithCallback');
  11.  
  12. // "Abstract" base c'tor
  13. function CoThreadBase(func, yieldEvery, thisCtx) {
  14.     this._thisCtx = thisCtx ? thisCtx : this;
  15.     
  16.     // default to 1
  17.     this._yieldEvery = typeof yieldEvery == 'number' ? Math.floor(yieldEvery) : 1;
  18.     if (yieldEvery < 1) {
  19.         throw Cr.NS_ERROR_INVALID_ARG;
  20.     }
  21.     
  22.     if (typeof func != 'function' && !(func instanceof Function)) {
  23.         throw Cr.NS_ERROR_INVALID_ARG;
  24.     } 
  25.     this._func = func;
  26. }
  27.  
  28. /**
  29.  * Constructs a new CoThread (aka. pseudo-thread).
  30.  * A CoThread will repeatedly call a specified function, but "breaking"
  31.  * the operation temporarily after a certain ammount of calls,
  32.  * so that the main thread gets a chance to process any outstanding
  33.  * events.
  34.  * 
  35.  * Example:
  36.  *        Components.utils.import('resource://dta/cothread.jsm');
  37.  *        new CoThread(
  38.  *            // What to do with each item?
  39.  *          // Print it!
  40.  *          function(count) document.write(count + "<br>") || (count < 30000),
  41.  *          // When to turn over Control?
  42.  *          // Each 1000 items
  43.  *          1000
  44.  *        ).run();
  45.  *   
  46.  * @param {Function} func Function to be called. Is passed call count as argument. Returning false will cancel the operation. 
  47.  * @param {Number} yieldEvery Optional. After how many items control should be turned over to the main thread
  48.  * @param {Object} thisCtx Optional. The function will be called in the scope of this object (or if omitted in the scope of the CoThread instance)
  49.  */
  50. function CoThread(func, yieldEvery, thisCtx) {
  51.     CoThreadBase.call(this, func, yieldEvery, thisCtx);
  52.     
  53.     // fake generator so we may use a common implementation. ;)
  54.     this._generator = (function() { for(;;) { yield null }; })();
  55. }
  56.  
  57. CoThread.prototype = {
  58.     
  59.     _idx: 0,
  60.     _ran: false,
  61.     _finishFunc: null,
  62.     
  63.     run: function CoThread_run() {
  64.         if (this._ran) {
  65.             throw new Error("You cannot run a CoThread/CoThreadListWalker instance more than once.");
  66.         }
  67.         this._ran = true;
  68.         
  69.         this._timer = new Timer(this, 10, TYPE_REPEATING_SLACK);        
  70.     },
  71.     
  72.     QueryInterface: function CoThread_QueryInterface(iid) {
  73.         if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsITimerCallback)) {
  74.             return this;
  75.         }
  76.         throw Cr.NS_ERROR_NO_INTERFACE;
  77.     },
  78.     
  79.     notify: function CoThread_notify() {
  80.         let y = this._yieldEvery;
  81.         let g = this._generator;
  82.         let f = this._func;
  83.         let ctx = this._thisCtx;
  84.         let callf = this._callf;
  85.         try {        
  86.             for (let i = 0; i < y; ++i) {
  87.                 if (!callf(ctx, g.next(), this._idx++, f)) {
  88.                     throw 'complete';
  89.                 }
  90.             }
  91.         }
  92.         catch (ex) {
  93.             this.cancel();
  94.         }
  95.     },
  96.     
  97.     cancel: function CoThread_cancel() {
  98.         this._timer.cancel();
  99.         if (this._finishFunc) {
  100.             this._finishFunc.call(this._thisCtx);
  101.         }        
  102.     },
  103.     
  104.     _callf: function CoThread__callf(ctx, item, idx, func) {
  105.         return func.call(ctx, idx);
  106.     }
  107. }
  108.  
  109. /**
  110.  * Constructs a new CoThreadListWalker (aka. pseudo-thread).
  111.  * A CoThreadListWalker will walk a specified list and call a specified function
  112.  * on each item, but "breaking" the operation temporarily after a
  113.  * certain ammount of processed items, so that the main thread may
  114.  * process any outstanding events.
  115.  * 
  116.  * Example:
  117.  *        Components.utils.import('resource://dta/cothread.jsm');
  118.  *        new CoThreadListWalker(
  119.  *            // What to do with each item?
  120.  *          // Print it!
  121.  *          function(item, idx) document.write(item + "/" + idx + "<br>") || true,
  122.  *          // What items?
  123.  *          // 0 - 29999
  124.  *          (function() { for (let i = 0; i < 30000; ++i) yield i; })(),
  125.  *          // When to turn over Control?
  126.  *          // Each 1000 items
  127.  *          1000,
  128.  *          null,
  129.  *          function() alert('done')
  130.  *        ).run();
  131.  *   
  132.  * @param {Function} func Function to be called on each item. Is passed item and index as arguments. Returning false will cancel the operation. 
  133.  * @param {Array/Generator} arrayOrGenerator Array or Generator object to be used as the input list 
  134.  * @param {Number} yieldEvery Optional. After how many items control should be turned over to the main thread
  135.  * @param {Object} thisCtx Optional. The function will be called in the scope of this object (or if omitted in the scope of the CoThread instance)
  136.  * @param {Function} finishFunc Optional. This function will be called once the operation finishes or is cancelled.
  137.  */
  138. function CoThreadListWalker(func, arrayOrGenerator, yieldEvery, thisCtx, finishFunc) {
  139.     CoThreadBase.call(this, func, yieldEvery, thisCtx);
  140.     
  141.     if (arrayOrGenerator instanceof Array) {
  142.         // make a generator
  143.         this._generator = (i for each (i in arrayOrGenerator));
  144.     }
  145.     else if (typeof arrayOrGenerator != 'function' && !(arrayOrGenerator instanceof Function)) {
  146.         this._generator = arrayOrGenerator;
  147.     }
  148.     else {
  149.         throw Cr.NS_ERROR_INVALID_ARG;
  150.     }
  151.     
  152.     this._finishFunc = finishFunc;
  153.     if (this._lastFunc && (typeof func != 'function' && !(func instanceof Function))) {
  154.         throw Cr.NS_ERROR_INVALID_ARG;
  155.     } 
  156. }
  157.  
  158. // not just b.prototype = a.prototype, because we wouldn't then be allowed to override methods 
  159. for (x in CoThread.prototype) {
  160.     CoThreadListWalker.prototype[x] = CoThread.prototype[x];
  161. }
  162. CoThreadListWalker.prototype._callf = function CoThreadListWalker__callf(ctx, item, idx, func) {
  163.     return func.call(ctx, item, idx);
  164. }
  165.